// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of CImpl.
//   $Id$


#include "stdafx.h"
#include "impl.h"
#include "XFormsElement.h"
#include "XFormsXFormsElement.h"
#include "XFormsApp.h"
#include "repeat.h"
#include "case.h"


extern CXFormsApp theApp;

#ifdef COUNT_CIMPL
unsigned long CImpl::sm_Count = 0;
#endif


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

//TODO: sort out these constructors.

CImpl::CImpl():
	m_pXFormsElement(NULL)
	,m_pXForms(NULL)
	,m_pParent(NULL)
	,m_pParentImpl(NULL)
	,m_pModel(NULL)
	,m_bisBound(false)
	,m_bIsWithinRepeat(false)
	,m_bWithinRepeatResolved(false)
	{
	#ifdef COUNT_CIMPL
	++sm_Count;
#endif
	CStringW sTmp;
	sTmp.Format(L"%X", theApp.FormsManager().TwosComplement(reinterpret_cast<long>(this)));
	m_sUniqueId = sTmp.GetString();
}

CImpl::CImpl(CXFormsXFormsElement * pXForms):
	m_pXForms(pXForms)
	,m_pParent(NULL)
	,m_pParentImpl(NULL)
	,m_pModel(NULL)
	,m_bisBound(false)
	,m_pXFormsElement(NULL)
	,m_bWithinRepeatResolved(false)
	,m_bIsWithinRepeat(false)
{
	#ifdef COUNT_CIMPL
	++sm_Count;
#endif
	CStringW sTmp;
	sTmp.Format(L"%X", theApp.FormsManager().TwosComplement(reinterpret_cast<long>(this)));
	m_sUniqueId = sTmp.GetString();
}

CImpl::CImpl(CXFormsElement* pElement):
	m_pXFormsElement(pElement)
	,m_pXForms(pElement->m_pXForms)
	,m_pParent(NULL)
	,m_pParentImpl(NULL)
	,m_pModel(NULL)
	,m_bisBound(false)
	,m_bWithinRepeatResolved(false)
	,m_bIsWithinRepeat(false)
{
#ifdef COUNT_CIMPL
	 ++sm_Count;
#endif
	m_pXFormsElement->AddRef();
	CStringW sTmp;
	sTmp.Format(L"%X", theApp.FormsManager().TwosComplement(reinterpret_cast<long>(this)));
	m_sUniqueId = sTmp.GetString();
}


/*
//PseudoAggregated functions.
HRESULT CImpl::addEventListener(BSTR type, IEventListener * listener, BOOL useCapture)
{
	return m_pEventTargetImpl?m_pEventTargetImpl->addEventListener(type,listener,useCapture):E_NOINTERFACE;
}
HRESULT CImpl::removeEventListener(BSTR type, IEventListener * listener, BOOL useCapture)
{
	return m_pEventTargetImpl?m_pEventTargetImpl->removeEventListener(type,listener,useCapture):E_NOINTERFACE;
}
HRESULT CImpl::dispatchEvent(IDOMEvent* evt)
{
	return m_pEventTargetImpl?m_pEventTargetImpl->dispatchEvent(evt):E_NOINTERFACE;
}
*/
STDMETHODIMP CImpl::QueryInterface(REFIID riid,void** ppvObject)
{
	HRESULT hr = E_NOINTERFACE;
	if(IID_IEventTarget == riid)
	{
		if(!m_pEventTargetImpl)
		{
			if(m_pXFormsElement)
			{
				hr = m_pXFormsElement->QueryInterface(riid,reinterpret_cast<void**>(&m_pEventTargetImpl));
			}

			if(!m_pEventTargetImpl)
				hr = m_pEventTargetImpl.CoCreateInstance(CLSID_EventTarget);
		}
		if(m_pEventTargetImpl)
		{
			hr = m_pEventTargetImpl.CopyTo(reinterpret_cast<IEventTarget**>(ppvObject));
		}

	}
	return hr;
}

CImpl::~CImpl()
{
	//Deregister the event handler for our UI component
	if(m_pXFormsElement)
	{
		m_pXFormsElement->formControlUnInitialize();
		::safeRelease(m_pXFormsElement);
	}
	POSITION pos = m_Children.GetStartPosition();
	CImpl * pImpl = 0;
	while(pos)
	{
		try
		{
			m_Children.GetNextAssoc(pos, pImpl, pImpl);
			pImpl->m_pParentImpl = 0;
			m_Children.RemoveKey(pImpl); 
		}
		catch(...){ASSERT(0);}
	}

	try
	{
		if(!m_Children.IsEmpty())
			m_Children.RemoveAll();
	}
	catch(...){ASSERT(0);}
	if(m_pXForms && m_sId.Length() > 0)
	{
		m_pXForms->Remove(m_sId);
	}

	m_sContent.Empty();

	if(m_pParentImpl)
	{
		//TODO: incorporate the idResolver remove in with 
		//	RemoveChild(this).
		m_pParentImpl->RemoveChild(this);
	}

	if(m_sId.Length() > 0)
	{
		if(m_pParentImpl)
		{
			//WARNING: At the moment, groups created as an unrolling
			//	of repeat, do not have Ids, so they will not get this far.
			//	This is a good thing, On the other hand,
			//	if these groups get an id, then they will start adding 
			//	themselves to themselves, as getIdResolver for repeat
			//	returns the resolver of the currently selected group.
			CIdResolver * pResolver = m_pParentImpl->GetIdResolver();
			if(pResolver)
				pResolver->Remove(m_sId);
		}
		else
		{
			//no parentImpl means that this item is in global
			//	scope.
			if(m_pXForms)
				m_pXForms->m_IdResolver.Remove(m_sId);
		}
	}
#ifdef COUNT_CIMPL
	--sm_Count;
#endif
}

HRESULT CImpl::RemoveChild(CImpl* pImpl)
{
	//was CImplGroup::
	HRESULT hr = S_FALSE;
    if(m_Children.RemoveKey(pImpl))
		hr = S_OK;
	return hr;
}

HRESULT CImpl::AddChild(CImpl *pChild)
{
	//Was CImplGroup, but had to be moved up.
	//Group needs to keep track of *all* of its children.  
	//	Group can be deleted at various times throughout
	//	execution, and even if a control is not reliant on
	// it for XPath execution context, 
	m_Children.SetAt(pChild, pChild);
	return S_OK;
}


long CImpl::getParentOrdinal()
{
	long l = -1;
	if(m_pXFormsElement)
	{
		l = m_pXFormsElement->getParentOrdinal();
	}
	return l;

}
#include "CaseUI.h"

bool  CImpl::IsWithinRepeat(CImpl * pImpl)
{
	bool bReturn = false;
	if(pImpl->m_bWithinRepeatResolved)
		bReturn = pImpl->m_bIsWithinRepeat;
	else if(pImpl->m_pParentImpl)
	{
		CImplRepeat * pRepeat = dynamic_cast<CImplRepeat *>(pImpl->m_pParentImpl);
		if(pRepeat)
			bReturn = true;
		else
		{
			CXFormsElementCase * pAsCase = dynamic_cast<CXFormsElementCase *>(pImpl->m_pParentImpl->m_pXFormsElement);
			if(pAsCase && !pAsCase->IsAppearanceFull())
				bReturn = true;
		}

		if(!bReturn)
			bReturn = IsWithinRepeat(pImpl->m_pParentImpl);
		//set catchy so that it doesn't have to climb the tree again
		pImpl->m_bIsWithinRepeat = bReturn;
		pImpl->m_bWithinRepeatResolved = true;
	}
	return bReturn;	
}

void CImpl::setModel(CImplModel*  pModel)
{
	const_cast<CImplModel*>(m_pModel) = pModel;
}

HRESULT CImpl::RunCustomBehaviour(const CComBSTR & sEventName)
{
	HRESULT hr = S_FALSE;
	try
	{

		CComPtr<IEventTarget> pEventTargetImpl;
		QueryInterface(IID_IEventTarget,(void**)&pEventTargetImpl);
		if(pEventTargetImpl)
		{
			CComPtr<IDOMEvent> evt;
			hr = theApp.FormsManager().CreateDOMEvent(&evt);
			if(SUCCEEDED(hr) && evt) {
				evt->initEvent(sEventName,FALSE,FALSE);
				BOOL bCancelled;
				if(!m_pXForms || IsAwaitingDeletion())
					hr = E_FAIL;
				else
					hr = pEventTargetImpl->dispatchEvent(evt,&bCancelled);
			}
		}
	}
	catch(_com_error)
	{
		ASSERT(0);
	}
	return hr;
}

HRESULT CImpl::RunDeferredUpdate(void)
{
	HRESULT hr = E_FAIL;
	if(m_pXForms)
		hr = m_pXForms->RunDeferredUpdate();
	return hr;
}

// adds the control to the appropriate id list.
void CImpl::AddToIdList(void)
{
	if(m_pParentImpl)
	{
		//WARNING: At the moment, groups created as an unrolling
		//	of repeat, do not have Ids, so they will not get this far.
		//	This is a good thing, On the other hand,
		//	if these groups get an id, then they will start adding 
		//	themselves to themselves, as getIdResolver for repeat
		//	returns the resolver of the currently selected group.
		m_pParentImpl->GetIdResolver()->AddItem(this);
	}
	else if(m_pParent)
	{
		m_pParent->GetIdResolver()->AddItem(this);
	}
	else
	{
		//no parentImpl means that this item is in global
		//	scope.
		m_pXForms->m_IdResolver.AddItem(this);
	}
}

bool CImpl::IsActive() const
{
	if(m_pParentImpl)
		return m_pParentImpl->IsActive();
	else
		return true;
}


HRESULT CImpl::initialize(bool /*bForce*/)
{
	//FUDGE: To prevent CImplModel from getting the wrong initialize.
	return S_OK;
}


HRESULT CImpl::init(CImpl * pParent)
{

	//Set the xforms member if that has not already been done.
	if(pParent && !m_pXForms)
	{
		m_pXForms = pParent->m_pXForms;
	}
	
	if(m_pParentImpl)
	{

		m_pParentImpl->AddChild(this);
	}
	//set the parent member.
	m_pParent = pParent;

	//add to the list of identified controls if applicable.
	if(!!m_sId && m_sId.m_str[0])
	{
		AddToIdList();
	}
	
	return S_OK;
}

CIdResolver * CImpl::GetIdResolver() const
{
	//only special derived clases will have their own
	//	idresolver, this function should be overridden 
	//	in thos classes to return that.
	if(m_pXForms && m_pXFormsElement && !m_pParentImpl)
	{
		m_pXFormsElement->ResolveEnclosingBinding();
	}

	if(m_pParentImpl)
	{
		//if we have a parentImpl, we may be in a scope boundary
		//	return the id resolver of the parent.
		return m_pParentImpl->GetIdResolver();
	}
	else if(m_pParent)
	{
		//If we have a parent, that may have a parentImpl that defines the scope of this ID.
		return m_pParent->GetIdResolver();
	}
	else if(m_pXForms)
	{
		//otherwise return that of the xforms element.
		return &(m_pXForms->m_IdResolver);
	}
	else
		return 0;
}


HRESULT CImpl::FireEvent(const CComBSTR & sEventName,bool /*bBubble*/,bool /*bCancelable*/,void * /*vdContext*/)
{

#if DEBUG_LEVEL & DEBUG_MONITOR_EVENTS
	AtlTrace("'formsplayer.dll': event fired: %s on %X id=%S\n",sEventName,this,m_sId);
#endif
	HRESULT hr = S_OK;
	//TODO: in the case of an unsatisfactory action,
	//	issue some kind of error to the User.
	
	bool bIsAlreadyPostponing = IsInDeletionPostponementState();
	if(!bIsAlreadyPostponing)
		EnterDeletionPostponementState();
	hr = RunCustomBehaviour(sEventName);
	if(SUCCEEDED(hr))
	{
		HRESULT _hr = RunDefaultBehaviour(sEventName);
		//DO not run deferred update, this is the job of Action!
		//if the default failed, pass that information out,
		//	If however, there was nothing to run, 
		//	pass the custom hr out.
		if(FAILED(_hr))
			hr = _hr;
	}
	if(!bIsAlreadyPostponing)
		ExitDeletionPostponementState();

	return hr;

}
